<?php
/**
 * templates parser
 * @static
 * Django's templating engine (partial) PHP implementation
 * @link http://djangoproject.com Django
 * this file is from a webappkit package (kit)
 * @link http://webappkit.net
 * @package djtpl
 * @author J.Ducastel <nospam0@ducastel.name>
 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
 */
class djtplParser {

    /**
    * splits template string into tokens.
    * @static
    * @param string $str
    * @return array
    */
    function tokenize(&$str) {
        $regex='/({%[^{}]+%})|({{[^{}]+}})/';
        $tokens=preg_split($regex,$str,-1,PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
        $max=count($tokens);
        for ($n=0; $n<$max; $n++) {
            $tokens[$n]=&new DjTplToken($tokens[$n]);
        }
        // print_r($tokens);
        return $tokens;
    }/**/

    /**
    * Builds next node from tokens array
    * @static
    * @param array &$tokens
    * @param DjTplTemplate $tpl owner template
    * @access public
    * @return djtplNode
    */
    function & tokensToNode(&$tokens, &$tpl) {
        // infinite loop protection
        static $ifni;
        $ifni++;
        if ($ifni>1000) {
            trigger_error("infinite loop ?");
            exit;
        }
        // preparing to build node
        $node=false;
        // checking next token
        if (!$token=array_shift($tokens) or !is_a($token,'djtplToken'))
            return $node;
        // building upon token type
        switch ($token->getType()) {
            case 'text':
                $node=new DjTplTextNode($tpl);
                $node->buildFromTokens($token);
                break;
            case 'var':
                $node=new DjTplVarNode($tpl);
                $node->buildFromTokens($token);
                break;
            case 'tag':
                // is tag registered ?
                $tag=$token->getTagName();
                $class=djtplParser::getTagNodeClass($tag);
                //$registered_tags=djangoTplParser::tags();
                if (class_exists($class)) {
                    // building appropriate node
                    $node=&new $class($tpl);
                    $node->buildFromTokens($token,$tokens);
                    break;
                }
                // unknown/unsupported tag, defaulting
                $node=&new DjTplTagNode($tpl);
                $node->buildFromTokens($token,$tokens);
                break;
            default:
                break;
        }
        return $node;
    }/**/

    /**
    * defines tag node class upon tag name
    * @param string $tag
    * @access public
    * @return string
    */
    function getTagNodeClass($tag) {
        return 'djtpltagnode_'.$tag;
    }/**/

    /**
    * build nodes from tokens until it reaches defined ending tag
    * @static
    * @param array &$tokens djtplToken
    * @param string $closingTag may accept several closing tags, separated by space
    * @access public
    * @return array djtplNode
    */
    function tokensToNodesUntilTag(&$tokens,$endtag='end',&$tpl) {
        $n=0; $nodes=array();
        // $endtags=explode(' ',$endtagsdef); echo "<p>$endtag</p>";
        while ($node=&DjTplParser::tokensToNode($tokens,$tpl)) {
            // infinite loop protection
            $n++; if ($n>500) break;
            // is node expected end tag ?
            if ($node->isTag($endtag)) {
                // reached closing tag
                // echo '<p>reached '.$endtag.'</p>';
                //return true;
                break;
            }
            $nodes[]=&$node;

        }
        return $nodes;
    }/**/

    /**
    * apply filter to value, or return value if failed
    * @static
    * @param mixed $value
    * @param string $filter filter definition
    * @access public
    * @return mixed
    */
    function applyFilter($value,$filter) {
        // parsing filter
        if (!ereg('^([^:]*)(:"(.*)")?$',$filter,$search)) {
            trigger_error("invalid filter $filter");
            return $value;
        }
        //echo wakdebug::vardump($search);
        $filterFunc=$search[1];
        $filterArg=$search[3];
        // searching for function
        if (function_exists('djtplfilter_'.$filterFunc)) {
            // using custom djangotpl filter func
            $func='djtplfilter_'.$filterFunc;
        } else if (function_exists($filterFunc)) {
            // backing to php native func
            $func=$filterFunc;
        } else {
            // no function found
            trigger_error("no function found for $filterFunc");
            return $value;
        }
        // function call with or without arg
        if ($filterArg) {
            $value=$func($value,$filterArg);
        } else {
            $value=$func($value);
        }
        return $value;
    }/**/

    /**
    * evals expression and return value
    * @static
    * expression may be path, quoted string
    * @param djtplContext &$context
    * @param string $expr expression to eval
    * @access public
    * @return mixed
    */
    function evalExpression(&$context,$expr) {
        //echo "<p>djangoTplParser::evalExpression($expr)</p>";
        $value=null;
        // is quoted string ?
        if (ereg('^"(.*)"$',$expr,$search))
            return $search[1];
        // is integer ?
        if (ereg('^[0-9]+$',$expr))
            return intval($expr);
        // is constant
        if (defined($expr))
            return constant($expr);
        // is var
        $value=$context->getpath($expr);
        if (!is_null($value))
            return $value;
        // echo wakdebug::vardump($value);
        // trigger_error("<p>no value for $expr !</p>");
        return $value;
    }/**/

}
